package cn.sw.common.utils;

import java.lang.reflect.*;
import java.util.*;

/**
 * 树形结构工具类
 */
public class TreeUtil {

    /**
     * 构建树形结构（无侵入式，基于反射）
     *
     * @param nodeList 扁平数据列表
     * @return 根节点列表（森林）
     */
    public static <T> List<T> buildTree(List<T> nodeList) throws Exception {
        if (nodeList == null || nodeList.isEmpty()) {
            return Collections.emptyList();
        }

        Class<?> clazz = nodeList.get(0).getClass();

        Method getIdMethod = getMethod(clazz, "getId");
        Method getParentIdMethod = getMethod(clazz, "getParentId");
        Method setChildrenMethod = getMethod(clazz, "setChildren", List.class);

        if (getIdMethod == null || getParentIdMethod == null || setChildrenMethod == null) {
            throw new IllegalArgumentException("实体必须包含 getId(), getParentId(), setChildren(List<?>) 方法");
        }

        Map<Object, T> nodeMap = new HashMap<>();
        for (T node : nodeList) {
            Object id = invokeMethod(node, getIdMethod);
            nodeMap.put(id, node);
        }

        List<T> rootNodes = new ArrayList<>();

        for (T node : nodeList) {
            Object parentId = invokeMethod(node, getParentIdMethod);

            if (parentId == null || !nodeMap.containsKey(parentId)) {
                rootNodes.add(node);
            } else {
                T parent = nodeMap.get(parentId);
                List<T> children = (List<T>) parent.getClass().getMethod("getChildren").invoke(parent);
                if (children == null) {
                    children = new ArrayList<>();
                    setChildrenMethod.invoke(parent, children);
                }
                children.add(node);
            }
        }

        return rootNodes;
    }

    private static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    private static Object invokeMethod(Object obj, Method method) {
        try {
            return method.invoke(obj);
        } catch (Exception e) {
            throw new RuntimeException("调用方法失败: " + method.getName(), e);
        }
    }
}
